/*
上传 模块
可以保存到 本地
也可以保存到 对象存储
对象存储采用 亚马逊 S3 服务
理论上只要是兼容AWS S3 的对象存储服务，都可以使用此方法进行上传。
对象存储未经过测试
本地上传采用 本地存储 服务
支持 jpeg png gif 格式图片上传
一定程度上杜绝了一些简单的文件上传漏洞
*/

package model

import (
	"fmt"
	"mime/multipart"
	"net/url"
	"os"
	"path/filepath"
	"time"

	"gitee.com/keeving/quanta-test-cat/utils"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/s3"
	"github.com/gin-gonic/gin"
	"gorm.io/gorm"
)

const (
	maxFileSize = 5 * 1024 * 1024    // 5MB
	bucketName  = "your-bucket-name" // 替换为你自己的 S3 存储桶名称
)

var (
	allowedFileTypes = []string{"image/jpeg", "image/png", "image/gif"}
	s3Client         *s3.S3
	localUploadPath  = utils.LocalUploadPath
)

var LocalURL string = "http://154.9.253.110:9090/"

func init() {
	// 从环境变量中获取 S3 配置
	endpoint := utils.EndPonit
	accessKey := utils.AccessKey
	secretKey := utils.SecretKey
	useSSL := utils.UseSSL

	// 初始化 S3 客户端
	sess, err := session.NewSession(&aws.Config{
		Region:           aws.String(utils.Region), // 可以设置为任意区域，因为我们将使用自定义端点
		Credentials:      credentials.NewStaticCredentials(accessKey, secretKey, ""),
		Endpoint:         aws.String(endpoint),
		S3ForcePathStyle: aws.Bool(true), // 使用路径样式 URL
		DisableSSL:       aws.Bool(!useSSL),
	})
	if err != nil {
		panic(err)
	}
	s3Client = s3.New(sess)

	// 确保存储桶存在
	_, err = s3Client.CreateBucket(&s3.CreateBucketInput{
		Bucket: aws.String(bucketName),
	})
	if err != nil {
		// 检查存储桶是否已经存在
		_, errBucketExists := s3Client.HeadBucket(&s3.HeadBucketInput{Bucket: aws.String(bucketName)})
		if errBucketExists == nil {
			fmt.Printf("We already own %s\n", bucketName)
		} else {
			fmt.Printf("Failed to create bucket: %s\n", err)
		}
	}
}

// UploadAFile 上传并返回 URL
func UploadFile(c *gin.Context, file *multipart.FileHeader, storageType string) string {
	// 检查文件大小
	if file.Size > maxFileSize {
		return "file size exceeds the limit"
		// fmt.Errorf("file size exceeds the limit")
	}

	// 检查文件类型
	if !contains(allowedFileTypes, file.Header.Get("Content-Type")) {
		return "unsupported file type"
		// fmt.Errorf("unsupported file type")
	}

	// 选择保存位置
	if storageType != "local" && storageType != "s3" {
		return "invalid storage_type, must be 'local' or 's3'"
		// fmt.Errorf("invalid storage_type, must be 'local' or 's3'")
	}

	var avatarURL string
	var err error
	if storageType == "local" {
		avatarURL, err = saveToLocal(c, file)
	} else {
		avatarURL, err = saveToS3(file)
	}

	if err != nil {
		return "failed to save uploaded file"
		// fmt.Errorf("failed to save uploaded file: %w", err)
	}
	return avatarURL
}

// UploadAvatar 上传并返回 URL
func UploadAvatar(c *gin.Context, userID string, file *multipart.FileHeader, storageType string) string {
	// 检查用户 ID
	if userID == "" {
		return "user_id is required"
		// fmt.Errorf("user_id is required")
	}

	// 检查文件大小
	if file.Size > maxFileSize {
		return "file size exceeds the limit"
		// fmt.Errorf("file size exceeds the limit")
	}

	// 检查文件类型
	if !contains(allowedFileTypes, file.Header.Get("Content-Type")) {
		return "unsupported file type"
		// fmt.Errorf("unsupported file type")
	}

	// 选择保存位置
	if storageType != "local" && storageType != "s3" {
		return "invalid storage_type, must be 'local' or 's3'"
		// fmt.Errorf("invalid storage_type, must be 'local' or 's3'")
	}

	var avatarURL string
	var err error
	if storageType == "local" {
		avatarURL, err = saveToLocal(c, file)
	} else {
		avatarURL, err = saveToS3(file)
	}

	if err != nil {
		return "failed to save uploaded file"
		// fmt.Errorf("failed to save uploaded file: %w", err)
	}

	// 更新用户的头像路径
	if err := UpdateUserAvatar(userID, avatarURL); err != nil {
		return "failed to update user avatar:"
		// fmt.Errorf("failed to update user avatar: %w", err)
	}

	return avatarURL
}

// 上传猫猫图片
func UploadCat(c *gin.Context, userID string, file *multipart.FileHeader, storageType string) string {
	// 检查用户 ID
	if userID == "" {
		return ""
		// fmt.Errorf("user_id is required")
	}

	// 检查文件大小
	if file.Size > maxFileSize {
		return ""
		// fmt.Errorf("file size exceeds the limit")
	}

	// 检查文件类型
	if !contains(allowedFileTypes, file.Header.Get("Content-Type")) {
		return ""
		// fmt.Errorf("unsupported file type")
	}

	// 选择保存位置
	if storageType != "local" && storageType != "s3" {
		return ""
		// fmt.Errorf("invalid storage_type, must be 'local' or 's3'")
	}

	var avatarURL string
	var err error
	if storageType == "local" {
		avatarURL, err = saveToLocal(c, file)
	} else {
		avatarURL, err = saveToS3(file)
	}

	if err != nil {
		// fmt.Errorf("failed to save uploaded file: %w", err)
		return ""
	}

	// 更新用户的头像路径
	if err := UpdateCatAvatar(userID, avatarURL); err != nil {
		return ""
		// fmt.Errorf("failed to update user avatar: %w", err)
	}

	return avatarURL
}

func contains(s []string, e string) bool {
	for _, a := range s {
		if a == e {
			return true
		}
	}
	return false
}

// 生成唯一的文件名
func generateUniqueFileName(originalName string) string {
	// 获取当前时间的时间戳
	timestamp := time.Now().UnixNano()
	// 生成唯一文件名
	return fmt.Sprintf("%d_%s", timestamp, originalName)
}

func saveToLocal(c *gin.Context, file *multipart.FileHeader) (string, error) {
	// 创建上传目录
	if err := os.MkdirAll(localUploadPath, os.ModePerm); err != nil {
		// fmt.Println("创建目录失败:")
		return "", err
	}

	// 生成唯一文件名
	uniqueFileName := generateUniqueFileName(file.Filename)
	dst := filepath.Join(localUploadPath, uniqueFileName)
	// 保存文件

	if err := c.SaveUploadedFile(file, dst); err != nil {
		return "", err
	}

	dst = LocalURL + dst
	return dst, nil
}

func saveToS3(file *multipart.FileHeader) (string, error) {
	// 读取文件内容
	src, err := file.Open()
	if err != nil {
		return "", err
	}
	defer src.Close()

	// 上传文件到 S3
	objectName := file.Filename
	_, err = s3Client.PutObject(&s3.PutObjectInput{
		Bucket:        aws.String(bucketName),
		Key:           aws.String(objectName),
		Body:          src,
		ContentType:   aws.String(file.Header.Get("Content-Type")),
		ContentLength: aws.Int64(file.Size),
		ACL:           aws.String("public-read"), // 公开访问
	})
	if err != nil {
		return "", err
	}

	// 生成文件 URL
	u, err := url.Parse(os.Getenv("S3_ENDPOINT"))
	if err != nil {
		return "", err
	}
	u.Scheme = "https"
	if !utils.UseSSL {
		u.Scheme = "http"
	}
	u.Path = filepath.Join(bucketName, objectName)

	return u.String(), nil
}

// UpdateUserAvatar 更新用户的头像路径
func UpdateUserAvatar(userID string, avatarURL string) error {
	// 更新用户的头像路径
	result := db.Model(&User{}).Where("username = ?", userID).Update("avatar", avatarURL)
	if result.Error != nil {
		return result.Error
	}
	return nil
}

// UpdateCatAvatar 更新猫猫的图片路径
func UpdateCatAvatar(catID string, avatarURL string) error {
	// 更新猫猫的图片路径
	result := db.Model(&Cat{}).Where("id = ?", catID).Update("Imgs", avatarURL)
	db.Model(&Cat{}).Where("id = ?", catID).Update("ImgNumber", gorm.Expr("ImgNumber + 1"))
	if result.Error != nil {
		return result.Error
	}
	return nil
}
